import numpy as np
import lamina
lamina = reload( lamina )
from lamina import Lamina
import mapy.model.properties
mapy.model.properties = reload( mapy.model.properties )
from mapy.model.properties import Properties
from mapy.constants import ONE, ZER, FLOAT
class Laminate( Properties ):
    
    #TODO update this __slots__
    #__slots__ = [] 

    """
    Attributes:
    ____________________________________________________________________________

    id         laminate property id
    laminates  list of laminates that can be part of this laminate
    plies      list of plies
    plyt       common ply thickness for this laminate
    t          total thickness of the laminate
    eq_fracts  list of porcentages of each orientation
    eq_thetas  list of angles corresponding to eq_fracts
    eq_matobjs list of materials corresponding to eq_fracts
    xiA        laminate parameters for extensional matrix A
    xiB        laminate parameters for extension-bending matrix B
    xiD        laminate parameters for bending matrix D
    A          laminate extension matrix
    B          laminate extension-bending matrix
    D          laminate bending matrix
    E          laminate transferse shear matrix
    ABD        laminate ABD matrix
    ABDE       laminate ABD matrix with transverse shear terms
    ____________________________________________________________________________

    Note: 
    ____________________________________________________________________________
    """
    def __init__(self):
        super( Laminate, self ).__init__()
        self.id   = None
        self.laminates = []
        self.plies   = []
        self.matobj = None
        self.t    = None
        self.plyt = None
        eq_thetas = None
        eq_fracts = None
        eq_matobjs = None
        self.e1   = None
        self.e2   = None
        self.e3   = None
        self.nu12 = None
        self.g12  = None
        self.g13  = None
        self.g23  = None
        self.xiA  = None
        self.xiB  = None
        self.xiD  = None
        self.A    = None
        self.B    = None
        self.D    = None
        self.E    = None
        self.ABD  = None
        self.ABDE = None

    def rebuild( self ):
        T = ZER
        for ply in self.plies:
            ply.rebuild()
            T += ply.t
        self.t = T

    def calc_lamination_parameters( self ):
        #
        xiA1, xiA2, xiA3, xiA4 = ZER, ZER, ZER, ZER
        #
        xiB1, xiB2, xiB3, xiB4 = ZER, ZER, ZER, ZER
        #
        xiD1, xiD2, xiD3, xiD4 = ZER, ZER, ZER, ZER
        # 
        xiE1, xiE2, xiE3, xiE4 = ZER, ZER, ZER, ZER
        #
        T = ZER
        #
        for ply in self.plies:
            T += ply.t
        self.t = T
        h0   = -T/2
        for ply in self.plies:
            #
            hk_1 =  h0
            h0   += ply.t
            hk   =  h0 
            #
            Afac  = ply.t / T
            Bfac  = ( 2. / T**2 ) * ( hk**2 - hk_1**2 )
            Dfac  = ( 4. / T**3 ) * ( hk**3 - hk_1**3 )
            Efac  = ( 1. / T    ) * ( hk    - hk_1    )# * (5./6) * (5./6)
            #
            cos2t = ply.cos2t
            cos4t = ply.cos4t 
            sin2t = ply.sin2t
            sin4t = ply.sin4t
            #
            xiA1  += Afac * cos2t
            xiA2  += Afac * sin2t
            xiA3  += Afac * cos4t
            xiA4  += Afac * sin4t
            #
            xiB1  += Bfac * cos2t
            xiB2  += Bfac * sin2t
            xiB3  += Bfac * cos4t
            xiB4  += Bfac * sin4t
            #
            xiD1  += Dfac * cos2t
            xiD2  += Dfac * sin2t
            xiD3  += Dfac * cos4t
            xiD4  += Dfac * sin4t
            #
            xiE1  += Efac * cos2t
            xiE2  += Efac * sin2t
            xiE3  += Efac * cos4t
            xiE4  += Efac * sin4t
        #
        self.xiA = np.array([ ONE, xiA1, xiA2, xiA3, xiA4], dtype=FLOAT)
        self.xiB = np.array([ ZER, xiB1, xiB2, xiB3, xiB4], dtype=FLOAT)
        self.xiD = np.array([ ONE, xiD1, xiD2, xiD3, xiD4], dtype=FLOAT)
        self.xiE = np.array([ ONE, xiE1, xiE2, xiE3, xiE4], dtype=FLOAT)

    def calc_ABDE_from_lamination_parameters( self ):
        # dummies used to unpack vector results
        du1, du2, du3, du4, du5, du6 = ZER, ZER, ZER, ZER, ZER, ZER
        # A matrix terms
        Axx,Ayy,Axy, du1,du2,du3, Ass,Axs,Ays =\
            ( self.t        ) * np.dot( self.matobj.u, self.xiA )
        # B matrix terms
        Bxx,Byy,Bxy, du1,du2,du3, Bss,Bxs,Bys =\
            ( self.t**2/4.  ) * np.dot( self.matobj.u, self.xiB )
        # D matrix terms
        Dxx,Dyy,Dxy, du1,du2,du3, Dss,Dxs,Dys =\
            ( self.t**3/12. ) * np.dot( self.matobj.u, self.xiD )
        # E matrix terms
        du1,du2,du3, Exx,Eyy,Exy, du4,du5,du6 =\
            ( self.t        ) * np.dot( self.matobj.u, self.xiE )
        #
        self.A = np.array( [[ Axx, Axy, Axs ],
                            [ Axy, Ayy, Ays ],
                            [ Axs, Ays, Ass ]], dtype=FLOAT )
        #
        self.B = np.array( [[ Bxx, Bxy, Bxs ],
                            [ Bxy, Byy, Bys ],
                            [ Bxs, Bys, Bss ]], dtype=FLOAT )
        #
        self.D = np.array( [[ Dxx, Dxy, Dxs ],
                            [ Dxy, Dyy, Dys ],
                            [ Dxs, Dys, Dss ]], dtype=FLOAT )
        #
        self.E = np.array( [[ Exx, Exy ],
                            [ Exy, Eyy ]], dtype=FLOAT )
        #
        self.ABD  = np.array( [[ Axx, Axy, Axs, Bxx, Bxy, Bxs ],
                               [ Axy, Ayy, Ays, Bxy, Byy, Bys ],
                               [ Axs, Ays, Ass, Bxs, Bys, Bss ],
                               [ Bxx, Bxy, Bxs, Dxx, Dxy, Dxs ],
                               [ Bxy, Byy, Bys, Dxy, Dyy, Dys ],
                               [ Bxs, Bys, Bss, Dxs, Dys, Dss ]], dtype=FLOAT )
        #
        self.ABDE = np.array( [[ Axx, Axy, Axs, Bxx, Bxy, Bxs, ZER, ZER ],
                               [ Axy, Ayy, Ays, Bxy, Byy, Bys, ZER, ZER ],
                               [ Axs, Ays, Ass, Bxs, Bys, Bss, ZER, ZER ],
                               [ Bxx, Bxy, Bxs, Dxx, Dxy, Dxs, ZER, ZER ],
                               [ Bxy, Byy, Bys, Dxy, Dyy, Dys, ZER, ZER ],
                               [ Bxs, Bys, Bss, Dxs, Dys, Dss, ZER, ZER ],
                               [ ZER, ZER, ZER, ZER, ZER, ZER, Exx, Exy ],
                               [ ZER, ZER, ZER, ZER, ZER, ZER, Exy, Eyy ]],
                               dtype=FLOAT )
   
    def force_balanced_LP( self ):
        ONE, xiA1, xiA2, xiA3, xiA4 = self.xiA
        self.xiA = np.array([ ONE, xiA1, ZER, xiA3, ZER], dtype=FLOAT)
        self.calc_ABDE_from_lamination_parameters()

    def force_symmetric_LP( self ):
        self.xiB = np.zeros(5)
        self.calc_ABDE_from_lamination_parameters()

    def force_balanced( self ):
        self.A[0][2] = ZER
        self.A[1][2] = ZER
        self.A[2][0] = ZER
        self.A[2][1] = ZER
        self.ABD[0][2] = ZER
        self.ABD[1][2] = ZER
        self.ABD[2][0] = ZER
        self.ABD[2][1] = ZER
        self.ABDE[0][2] = ZER
        self.ABDE[1][2] = ZER
        self.ABDE[2][0] = ZER
        self.ABDE[2][1] = ZER

    def force_symmetric( self ):
        self.B = np.zeros((3,3))
        for i in range(0,3):
            for j in range(3,6):
                self.ABD[i][j]  = ZER
                self.ABDE[i][j] = ZER
        for i in range(3,6):
            for j in range(0,3):
                self.ABD[i][j]  = ZER
                self.ABDE[i][j] = ZER

    def read_stack( self, stack, plyt, laminaprop = None ):
        self.plyt = plyt
        import mapy
        mapy = reload( mapy )
        self.matobj = mapy.model.materials.matlamina.read_laminaprop( laminaprop )
        self.stack = stack
        for theta in self.stack:
            ply = Lamina()  
            ply.theta = np.array( theta, dtype=FLOAT )
            ply.t = np.array( self.plyt, dtype=FLOAT )
            ply.matobj = self.matobj
            self.plies.append( ply )
        self.rebuild()
        self.calc_lamination_parameters()
        self.calc_ABDE_from_lamination_parameters()
        return self 

    def read_lamination_parameters( xiA1, xiA2, xiA3, xiA4,
                                    xiB1, xiB2, xiB3, xiB4,
                                    xiD1, xiD2, xiD3, xiD4,
                                    xiE1, xiE2, xiE3, xiE4, laminaprop = None ):
        import mapy
        mapy = reload( mapy )
        lam.matobj = mapy.model.materials.matlamina.read_laminaprop( laminaprop )
        self.xiA = np.array([ ONE, xiA1, xiA2, xiA3, xiA4 ], dtype=FLOAT)
        self.xiB = np.array([ ZER, xiB1, xiB2, xiB3, xiB4 ], dtype=FLOAT)
        self.xiD = np.array([ ONE, xiD1, xiD2, xiD3, xiD4 ], dtype=FLOAT)
        self.xiE = np.array([ ONE, xiE1, xiE2, xiE3, xiE4 ], dtype=FLOAT)

def read_stack( stack, plyt, laminaprop = None ):
    lam = Laminate()
    lam.read_stack( stack, plyt, laminaprop )
    return lam 

def read_lamination_parameters( xiA1, xiA2, xiA3, xiA4,
                                xiB1, xiB2, xiB3, xiB4,
                                xiD1, xiD2, xiD3, xiD4,
                                xiE1, xiE2, xiE3, xiE4, laminaprop = None ):
    lam = Laminate()
    lam.read_lamination_parameters( xiA1, xiA2, xiA3, xiA4,
                                    xiB1, xiB2, xiB3, xiB4,
                                    xiD1, xiD2, xiD3, xiD4,
                                    xiE1, xiE2, xiE3, xiE4, laminaprop )
    return lam

